<template>
	<div ref="containerRef" class="container" @scroll="handleScroll">
		<div class="container-list" :style="scrollStyle" ref="listRef">
			<slot name="render" :list="renderList" :startIndex="startIndex">
				<template v-for="(item, index) in renderList" :key="item[rowKey]">
					<slot :item="item" :index="startIndex + index"></slot>
				</template>
			</slot>
		</div>
	</div>
</template>
<script setup>
import { ref, computed, watch, onMounted, onUpdated, onBeforeUnmount } from 'vue'

const {list, itemHeight} = defineProps({
  list: { // 渲染的数据
    type: Array,
    default: () => [],
  },
  rowKey: {
    type: [String, Number],
    default: 'id',
  },
  itemHeight: { // 预估每一项的高度, 宜少不宜多
    type: Number,
    default: 100,
  },
})
const containerRef = ref(null);
const listRef = ref(null);
const startIndex = ref(0);
const containerHeight = ref(0);
const position = ref([]);

const scrollTop = computed(()=> startIndex.value > 0 ? position.value[startIndex.value-1].bottom : 0);
const listHeight = computed(() => position.value[position.value.length - 1].bottom);

const scrollStyle = computed(() => {
	return {
		height:`${listHeight.value - scrollTop.value}px`,
		transform: `translate3d(0, ${scrollTop.value}px, 0)`,
	}
})

const renderCount = computed(() => Math.ceil(containerHeight.value/itemHeight))
const endIndex = computed(() => startIndex.value + renderCount.value)

const renderList = computed(() => {
	return list.slice(startIndex.value, endIndex.value);
})

let resizeObserver;
onMounted(() => {
	containerHeight.value = containerRef.value.clientHeight || 0;

    resizeObserver = new ResizeObserver((entries) => {
        for (const entry of entries) {
            const { height } = entry.contentRect
            if (height !== containerHeight.value) {
                containerHeight.value = height
            }
        }
    })
    
    if (containerRef.value) {
        resizeObserver.observe(containerRef.value)
    }
})
onUpdated(() => {
	updatePosition();
})
onBeforeUnmount(() => {
    resizeObserver?.disconnect?.();
})

watch(() => list, () => {
	initPosition();
},{
	immediate: true
})
function initPosition() {
	position.value = [];
	// console.log(list);
	list.forEach((d, i) => {
		position.value.push({
			index: i,
			height: itemHeight,
			top: i * itemHeight,
			bottom: (i + 1) * itemHeight,
		});
	});
}
function updatePosition(){
	//获取listRef下的子元素
  const nodes = listRef.value ? listRef.value.children : [];
	if(!nodes?.length) return;
	const data = [...nodes];
	// console.log(nodes)
	// 遍历所有的子元素更新真实的高度
	data.forEach(el => {
		let index = +el.getAttribute('itemid');
		const realHeight = el.getBoundingClientRect().height;
		// 判断默认的高度和真实的高度之差
		let diffVal = position.value[index].height - realHeight;
		if (diffVal !== 0) {
			for(let i = index; i < position.value.length; i++) {
				position.value[i].height = realHeight;
				position.value[i].top = position.value[i].top - diffVal;
				position.value[i].bottom = position.value[i].bottom - diffVal;
			}
		}
	})
}
let scrollTimer = null;
function handleScroll(e) {
    if (scrollTimer) return
  
    scrollTimer = requestAnimationFrame(() => {
        // 滚动处理逻辑
        const scrollTop = e.target.scrollTop;
	    startIndex.value = getStartIndex(scrollTop);
        
        cancelAnimationFrame(scrollTimer)
        scrollTimer = null
    })
    
}

const getStartIndex = (scrollTop) => {
	let left = 0;
	let right = position.value.length - 1;
	while (left <= right) {
		const mid = Math.floor((left + right) / 2);
		if(position.value[mid].bottom == scrollTop) {
			return mid + 1;
		} else if (position.value[mid].bottom > scrollTop) {
			right = mid - 1;
		} else if (position.value[mid].bottom < scrollTop) {
			left = mid + 1;
		}
	}
	return left;
}

</script>
<style scoped>
.container {
  width: 100%;
  height: 100%;
  overflow: auto;
}
.container-list{
    width: 100%;
}
</style>
